/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.

Licensed under the Oculus Master SDK License Version 1.0 (the "License"); you may not use
the Utilities SDK except in compliance with the License, which is provided at the time of installation
or download, or which otherwise accompanies this software in either electronic or hard copy form.

You may obtain a copy of the License at
https://developer.oculus.com/licenses/oculusmastersdk-1.0/

Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;

using Debug = UnityEngine.Debug;

public class OVRSystemPerfMetrics
{
	public const int TcpListeningPort = 32419;
	public const int PayloadTypeMetrics = 100;

	public const int MaxBufferLength = 65536;
	public const int MaxMessageLength = MaxBufferLength - sizeof(int);

	public class PerfMetrics
	{
		public int frameCount;
		public float frameTime;
		public float deltaFrameTime;

		public bool appCpuTime_IsValid;
		public float appCpuTime;
		public bool appGpuTime_IsValid;
		public float appGpuTime;
		public bool compositorCpuTime_IsValid;
		public float compositorCpuTime;
		public bool compositorGpuTime_IsValid;
		public float compositorGpuTime;
		public bool compositorDroppedFrameCount_IsValid;
		public int compositorDroppedFrameCount;
		public bool systemGpuUtilPercentage_IsValid;
		public float systemGpuUtilPercentage;
		public bool systemCpuUtilAveragePercentage_IsValid;
		public float systemCpuUtilAveragePercentage;
		public bool systemCpuUtilWorstPercentage_IsValid;
		public float systemCpuUtilWorstPercentage;
		public bool deviceCpuClockFrequencyInMHz_IsValid;
		public float deviceCpuClockFrequencyInMHz;
		public bool deviceGpuClockFrequencyInMHz_IsValid;
		public float deviceGpuClockFrequencyInMHz;
		public bool deviceCpuClockLevel_IsValid;
		public int deviceCpuClockLevel;
		public bool deviceGpuClockLevel_IsValid;
		public int deviceGpuClockLevel;

		public string ToJSON()
		{
			OVRSimpleJSON.JSONObject jsonNode = new OVRSimpleJSON.JSONObject();
			jsonNode.Add("frameCount", new OVRSimpleJSON.JSONNumber(frameCount));
			jsonNode.Add("frameTime", new OVRSimpleJSON.JSONNumber(frameTime));
			jsonNode.Add("deltaFrameTime", new OVRSimpleJSON.JSONNumber(deltaFrameTime));
			if (appCpuTime_IsValid)
			{
				jsonNode.Add("appCpuTime", new OVRSimpleJSON.JSONNumber(appCpuTime));
			}
			if (appGpuTime_IsValid)
			{
				jsonNode.Add("appGpuTime", new OVRSimpleJSON.JSONNumber(appGpuTime));
			}
			if (compositorCpuTime_IsValid)
			{
				jsonNode.Add("compositorCpuTime", new OVRSimpleJSON.JSONNumber(compositorCpuTime));
			}
			if (compositorGpuTime_IsValid)
			{
				jsonNode.Add("compositorGpuTime", new OVRSimpleJSON.JSONNumber(compositorGpuTime));
			}
			if (compositorDroppedFrameCount_IsValid)
			{
				jsonNode.Add("compositorDroppedFrameCount", new OVRSimpleJSON.JSONNumber(compositorDroppedFrameCount));
			}
			if (systemGpuUtilPercentage_IsValid)
			{
				jsonNode.Add("systemGpuUtilPercentage", new OVRSimpleJSON.JSONNumber(systemGpuUtilPercentage));
			}
			if (systemCpuUtilAveragePercentage_IsValid)
			{
				jsonNode.Add("systemCpuUtilAveragePercentage", new OVRSimpleJSON.JSONNumber(systemCpuUtilAveragePercentage));
			}
			if (systemCpuUtilWorstPercentage_IsValid)
			{
				jsonNode.Add("systemCpuUtilWorstPercentage", new OVRSimpleJSON.JSONNumber(systemCpuUtilWorstPercentage));
			}
			if (deviceCpuClockFrequencyInMHz_IsValid)
			{
				jsonNode.Add("deviceCpuClockFrequencyInMHz", new OVRSimpleJSON.JSONNumber(deviceCpuClockFrequencyInMHz));
			}
			if (deviceGpuClockFrequencyInMHz_IsValid)
			{
				jsonNode.Add("deviceGpuClockFrequencyInMHz", new OVRSimpleJSON.JSONNumber(deviceGpuClockFrequencyInMHz));
			}
			if (deviceCpuClockLevel_IsValid)
			{
				jsonNode.Add("deviceCpuClockLevel", new OVRSimpleJSON.JSONNumber(deviceCpuClockLevel));
			}
			if (deviceGpuClockLevel_IsValid)
			{
				jsonNode.Add("deviceGpuClockLevel", new OVRSimpleJSON.JSONNumber(deviceGpuClockLevel));
			}
			string str = jsonNode.ToString();
			return str;
		}

		public bool LoadFromJSON(string json)
		{
			OVRSimpleJSON.JSONObject jsonNode = OVRSimpleJSON.JSONObject.Parse(json) as OVRSimpleJSON.JSONObject;
			if (jsonNode == null)
			{
				return false;
			}
			frameCount = jsonNode["frameCount"] != null ? jsonNode["frameCount"].AsInt : 0;
			frameTime = jsonNode["frameTime"] != null ? jsonNode["frameTime"].AsFloat : 0;
			deltaFrameTime = jsonNode["deltaFrameTime"] != null ? jsonNode["deltaFrameTime"].AsFloat : 0;
			appCpuTime_IsValid = jsonNode["appCpuTime"] != null;
			appCpuTime = appCpuTime_IsValid ? jsonNode["appCpuTime"].AsFloat : 0;
			appGpuTime_IsValid = jsonNode["appGpuTime"] != null;
			appGpuTime = appGpuTime_IsValid ? jsonNode["appGpuTime"].AsFloat : 0;
			compositorCpuTime_IsValid = jsonNode["compositorCpuTime"] != null;
			compositorCpuTime = compositorCpuTime_IsValid ? jsonNode["compositorCpuTime"].AsFloat : 0;
			compositorGpuTime_IsValid = jsonNode["compositorGpuTime"] != null;
			compositorGpuTime = compositorGpuTime_IsValid ? jsonNode["compositorGpuTime"].AsFloat : 0;
			compositorDroppedFrameCount_IsValid = jsonNode["compositorDroppedFrameCount"] != null;
			compositorDroppedFrameCount = compositorDroppedFrameCount_IsValid ? jsonNode["ompositorDroppedFrameCount"].AsInt : 0;
			systemGpuUtilPercentage_IsValid = jsonNode["systemGpuUtilPercentage"] != null;
			systemGpuUtilPercentage = systemGpuUtilPercentage_IsValid ? jsonNode["systemGpuUtilPercentage"].AsFloat : 0;
			systemCpuUtilAveragePercentage_IsValid = jsonNode["systemCpuUtilAveragePercentage"] != null;
			systemCpuUtilAveragePercentage = systemCpuUtilAveragePercentage_IsValid ? jsonNode["systemCpuUtilAveragePercentage"].AsFloat : 0;
			systemCpuUtilWorstPercentage_IsValid = jsonNode["systemCpuUtilWorstPercentage"] != null;
			systemCpuUtilWorstPercentage = systemCpuUtilWorstPercentage_IsValid ? jsonNode["systemCpuUtilWorstPercentage"].AsFloat : 0;
			deviceCpuClockFrequencyInMHz_IsValid = jsonNode["deviceCpuClockFrequencyInMHz"] != null;
			deviceCpuClockFrequencyInMHz = deviceCpuClockFrequencyInMHz_IsValid ? jsonNode["deviceCpuClockFrequencyInMHz"].AsFloat : 0;
			deviceGpuClockFrequencyInMHz_IsValid = jsonNode["deviceGpuClockFrequencyInMHz"] != null;
			deviceGpuClockFrequencyInMHz = deviceGpuClockFrequencyInMHz_IsValid ? jsonNode["deviceGpuClockFrequencyInMHz"].AsFloat : 0;
			deviceCpuClockLevel_IsValid = jsonNode["deviceCpuClockLevel"] != null;
			deviceCpuClockLevel = deviceCpuClockLevel_IsValid ? jsonNode["deviceCpuClockLevel"].AsInt : 0;
			deviceGpuClockLevel_IsValid = jsonNode["deviceGpuClockLevel"] != null;
			deviceGpuClockLevel = deviceGpuClockLevel_IsValid ? jsonNode["deviceGpuClockLevel"].AsInt : 0;
			return true;
		}
	}

	public class OVRSystemPerfMetricsTcpServer : MonoBehaviour
	{
		public static OVRSystemPerfMetricsTcpServer singleton = null;

		private OVRNetwork.OVRNetworkTcpServer tcpServer = new OVRNetwork.OVRNetworkTcpServer();

		public int listeningPort = OVRSystemPerfMetrics.TcpListeningPort;

		void OnEnable()
		{
			if (singleton != null)
			{
				Debug.LogError("Mutiple OVRSystemPerfMetricsTcpServer exists");
				return;
			}
			else
			{
				singleton = this;
			}

			if (Application.isEditor)
			{
				Application.runInBackground = true;
			}
			tcpServer.StartListening(listeningPort);
		}

		void OnDisable()
		{
			tcpServer.StopListening();

			singleton = null;

			Debug.Log("[OVRSystemPerfMetricsTcpServer] server destroyed");
		}

		private void Update()
		{
			if (tcpServer.HasConnectedClient())
			{
				PerfMetrics metrics = GatherPerfMetrics();
				string json = metrics.ToJSON();
				byte[] bytes = Encoding.UTF8.GetBytes(json);
				tcpServer.Broadcast(OVRSystemPerfMetrics.PayloadTypeMetrics, bytes);
			}
		}

		PerfMetrics GatherPerfMetrics()
		{
			PerfMetrics metrics = new PerfMetrics();

			metrics.frameCount = Time.frameCount;
			metrics.frameTime = Time.unscaledTime;
			metrics.deltaFrameTime = Time.unscaledDeltaTime;

			float? floatValue;
			int? intValue;

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.App_CpuTime_Float);
			metrics.appCpuTime_IsValid = floatValue.HasValue;
			metrics.appCpuTime = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.App_GpuTime_Float);
			metrics.appGpuTime_IsValid = floatValue.HasValue;
			metrics.appGpuTime = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.Compositor_CpuTime_Float);
			metrics.compositorCpuTime_IsValid = floatValue.HasValue;
			metrics.compositorCpuTime = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.Compositor_GpuTime_Float);
			metrics.compositorGpuTime_IsValid = floatValue.HasValue;
			metrics.compositorGpuTime = floatValue.GetValueOrDefault();

			intValue = OVRPlugin.GetPerfMetricsInt(OVRPlugin.PerfMetrics.Compositor_DroppedFrameCount_Int);
			metrics.compositorDroppedFrameCount_IsValid = intValue.HasValue;
			metrics.compositorDroppedFrameCount = intValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.System_GpuUtilPercentage_Float);
			metrics.systemGpuUtilPercentage_IsValid = floatValue.HasValue;
			metrics.systemGpuUtilPercentage = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.System_CpuUtilAveragePercentage_Float);
			metrics.systemCpuUtilAveragePercentage_IsValid = floatValue.HasValue;
			metrics.systemCpuUtilAveragePercentage = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.System_CpuUtilWorstPercentage_Float);
			metrics.systemCpuUtilWorstPercentage_IsValid = floatValue.HasValue;
			metrics.systemCpuUtilWorstPercentage = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.Device_CpuClockFrequencyInMHz_Float);
			metrics.deviceCpuClockFrequencyInMHz_IsValid = floatValue.HasValue;
			metrics.deviceCpuClockFrequencyInMHz = floatValue.GetValueOrDefault();

			floatValue = OVRPlugin.GetPerfMetricsFloat(OVRPlugin.PerfMetrics.Device_GpuClockFrequencyInMHz_Float);
			metrics.deviceGpuClockFrequencyInMHz_IsValid = floatValue.HasValue;
			metrics.deviceGpuClockFrequencyInMHz = floatValue.GetValueOrDefault();

			intValue = OVRPlugin.GetPerfMetricsInt(OVRPlugin.PerfMetrics.Device_CpuClockLevel_Int);
			metrics.deviceCpuClockLevel_IsValid = intValue.HasValue;
			metrics.deviceCpuClockLevel = intValue.GetValueOrDefault();

			intValue = OVRPlugin.GetPerfMetricsInt(OVRPlugin.PerfMetrics.Device_GpuClockLevel_Int);
			metrics.deviceGpuClockLevel_IsValid = intValue.HasValue;
			metrics.deviceGpuClockLevel = intValue.GetValueOrDefault();

			return metrics;
		}

	}
}
